/*
程序说明：
作者：木
参考：度一教育
时间：21-10-26
*/
//Mine构造函数
function Mine(tr,td,mineNum){
this.tr = tr; //行数
this.td = td; //列数
this.mineNum = mineNum; //雷的数量
this.squares = []; //存储所有方块的信息，它是一个二位数组，按行和列的顺序排放，存取都用行列的形式
this.tds = [];     //存储所有的单元格的DOM对象
this.surplusMine = mineNum; //剩余雷的数量
this.allRight = false; //右击标出来的红旗是否都是雷，用来判断用户是否成功

this.parent = document.querySelector('.gameBox');//表明程序所有插入元素的父级元素
}

//添加方法
//生成n个不重复的雷，返回一个一位数组(先将数组按顺序初始化，然后随机打乱顺序，最后截取前mineNum位作为随机雷的位置)
Mine.prototype.randomNum = function(){
    var square = new Array(this.tr*this.td) //生成空数组，长度为总方块数
    for ( var i= 0;i<square.length ;i++){
        square[i]=i;
    }
    square.sort(function(){return 0.5-Math.random()});
    return square.slice(0,this.mineNum);
}
//初始化
Mine.prototype.init = function(){
    var rn = this.randomNum();//雷在方格中的位置
    console.log(rn);
    var n = 0;//用来找到对应的索引
    for(var i=0;i<this.tr;i++){
        this.squares[i]=[];
        for(var j=0;j<this.td;j++){
            //取一个方块在数组里的数据要用行与列的形式去取，找方块周围的方块的时候要用坐标的形式去取。行与列的形式跟坐标的形式{x,y}刚好相反
            if(rn.indexOf(++n)!=-1){//indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
                //如果这个条件成立，说明现在循环到的这个索引是个雷
                this.squares[i][j] = {type:'mine',x:j,y:i};
            }else{
                this.squares[i][j] = {type:'number',x:j,y:i,value:0};
            }
        }
    }
    this.updateNum();

    this.createDom();  
    
    //关闭右键菜单
    this.parent.oncontextmenu = function(){//oncontextmenu 事件在元素中用户右击鼠标时触发并打开上下文菜单。
        return false;
    }

    //剩余的雷数
    this.mineNumDom = document.querySelector('.mineNum');
    this.mineNumDom.innerHTML = this.surplusMine;

}

//创建表格
Mine.prototype.createDom = function(){
    This=this;
    var table = document.createElement('table');

    for(var i=0 ;i<this.tr ;i++){//行
        var domTr = document.createElement('tr');
        this.tds[i]=[];
        for(var j=0 ;j<this.td ;j++){//列
            var domTd = document.createElement('td');
            domTd.pos = [i,j];  //把这个格子对应的行与列存到格子上，
            domTd.onmousedown = function(event){
                This.play(event,this);//This 是实例对象 this 指点击的那个td
            }

            this.tds[i][j]=domTd;//这里是把所有创建的td添加到数组当中

            domTr.appendChild(domTd);
        }

        table.appendChild(domTr);
    }

    this.parent.innerHTML="";//避免创建多个
    this.parent.appendChild(table);
}

//找某个放个周围的8个方格
Mine.prototype.getAround = function(square){
    var x = square.x;
    var y = square.y;
    var result = [];//找到的格子的坐标返回出去，也是一个二位数组
    /*
        x-1,y-1      x,y-1      x+1 ,y-1
        x-1,y        x,y        x+1 ,y
        x-1,y+1      x,y+1      x+1 ,y+1
    */
    for(var i =x-1; i<=x+1;i++){
        for(var j = y-1; j<=y+1; j++){
            if( i<0 || j<0 || i>this.td-1 ||  j>this.tr-1 || (i==x&&j==y) || this.squares[j][i].type=='mine' ){
                continue;
            }
            result.push([j,i]);
        }
    }
    return result;
}

//更新所有的数字
Mine.prototype.updateNum = function(){
    for(var i=0 ;i<this.tr ;i++){
        for(var j=0;j<this.td;j++){
            //只更新的是雷周围的数字
            if(this.squares[i][j].type == 'number'){
                continue;
            }
            var num = this.getAround(this.squares[i][j]);//获取每一个雷周围的数字
            for(var k=0;k<num.length;k++){//利用num数组更新每个方块的数字
                this.squares[num[k][0]][num[k][1]].value+=1;
            }
        }
    } 
}

//开始玩
Mine.prototype.play = function(ev,obj){
    This=this;
    
    //点击左键,后面的条件表明用户插完红旗以后就不能左键了
    if(ev.which == 1 && obj.className!='flag'){
        var curSquare = this.squares[obj.pos[0]][obj.pos[1]];
        var cl=['zero','one','two','three','four','five','six','seven','eight'];
        //点到了数字
        if(curSquare.type=='number'){
            obj.innerHTML = curSquare.value;    //值
            obj.className = cl[curSquare.value];//样式

            //点到了数字零
            if(curSquare.value==0){
                obj.innerHTML='';//数字零不显示，直接显示空
                //找四周，显示四周，如果周围的数字不是零就显示到这里
                //如果值为零 ，以搜索到的节点为中间递归上一步
                function getAllZero(square){
                    var around = This.getAround(square);//找到了周围的0~8个格子

                    for (var i=0;i<around.length;i++){
                        
                        var x=around[i][0];//行
                        var y=around[i][1];//列

                        This.tds[x][y].className = cl[This.squares[x][y].value];//给样式

                        if(This.squares[x][y].value==0){//其中的零值方块另处理

                            if(!This.tds[x][y].check){//排除搜索上一层方块的可能性
                                This.tds[x][y].check=true;
                                getAllZero(This.squares[x][y]);
                            }
                            
                        }
                        //找到的不为零，就把人家的值显示出来
                        else{
                            This.tds[x][y].innerHTML = This.squares[x][y].value;
                        }
                    }
                }

                getAllZero(curSquare);
            }

        //点到了炸弹    
        }else{
            this.gameOvar(obj);
        }

        
    }else if(ev.which ==3){  //点击右键
        
        //如果右键的是一个数字，那就不能点击。
        if(obj.className && obj.className!='flag'){
            raturn;
        }

        //有其子，去旗子，无旗子，加旗子
        obj.className = obj.className=='flag'?'':'flag';

        //如果用户成功的把旗子插到炸弹位置，维护allRight = true 否则为 false。当且仅当最后allRghit == true 为游戏胜利
        if(this.squares[obj.pos[0]][obj.pos[1]].type=='mine'){
            this.allRight=true;
        }else{
            this.allRight=false;
        }

        //插一个旗子减少剩雷数，拔一个旗子增加剩雷数
        if(obj.className=='flag'){
            this.mineNumDom.innerHTML = --this.surplusMine;
        }else{
            this.mineNumDom.innerHTML = ++this.surplusMine;
        }

        //插完所有旗子以后判断
        if(this.surplusMine==0){

            if(this.allRight){
                //全部插对了
                alert("恭喜你，游戏成功");
            }else{//有没插对的旗子
                alert("游戏失败");
                this.gameOvar();
            }
        }  
    }
}

//游戏失败
Mine.prototype.gameOvar = function(clickTd){

    //显示所有的雷
    for (var i=0;i<this.tr;i++){
        for(var j=0;j<this.td;j++){
            if(this.squares[i][j].type=='mine'){
                this.tds[i][j].className='mine';
            }

            //取消所有格子的点击事件
            this.tds[i][j].onmousedown=null;
        }
    }

    //给点中的雷标红
    if(clickTd){
        clickTd.style.backgroundColor='red';
    }
}

//设置上边button的功能

var btns = document.querySelectorAll('.level button');  //选中所有button
var mine = null;                                        //实例
var ln = 0;                                             //维护当前选中状态
var arr = [[9,9,10],[16,16,40],[28,28,99]];             //不同等级的不同参数{长，宽，炸弹数}

//给三个button设置onclick事件
for (let i=0;i<btns.length-1;i++){
    btns[i].onclick=function(){
        btns[ln].className='';      //去点击前button的样式
        this.className='active';    //给被点击button加样式
        mine= new Mine(...arr[i]);  //new 对象
        mine.init();                //初始化
        ln=i;                       //维护当前选中button
    }
}
//刚打开页面的初始状态
btns[0].onclick();

//设置重新开始button
btns[3].onclick = function(){
    mine.surplusMine=arr[ln][2];    //当点击重新开始时剩雷数surplusMine = 当前维护对象的 第三个参数
    mine.init();                    //重新初始化  
}


